昨天在講格式驗證的時候有提到 Node.js 的 validator 跟 Go 的 govalidator 有很多好用的功能可以用,像是幫你驗證 isEmail()
、isMobilePhone()
之類的,但萬一你要的功能沒有在裡面呢?譬如說你想要驗證的格式是台灣自來水公司的水號、或是公司自訂的產品序號等等,這時就需要出動 regular expression 了,而今天就是要來說說在寫 regex 時有哪些該注意的地方
怕有人太久沒用已經快忘記 regex 的語法XD,這邊簡單回顧一下:regex 是可以用來比對字串是不是符合某個 pattern 的工具,譬如說手機號碼是十個數字,那我就可以寫 [0-9]{10}
,而信箱是中間有 @
的任意字串,那就可以寫成 .+@.+
,如果還是想不起來的話我找了一個 Cheat Sheet,看一看應該就會用了~
+
跟 *
首先,因為任何類型的資料一定都有他的適當長度,所以應該用更確切的長度範圍來做限制而不是用 +
或 *
譬如說上面信箱 .+@.+
的例子,你的程式應該不可能接受 a@b
這麼短的信箱吧,反之當然也不可能接受長到一萬字的信箱,所以你至少應該把它改成 .{2,50}@.{5,50}
這個比較合理的範圍
那為什麼要這麼講究範圍呢?因為爆長或爆短的資料通常都不會是正常的資料,比較可能是攻擊者刻意造出來試探的。如果這些資料不幸進到資料庫可能會導致預期外的錯誤
.
除了不要用 +
跟 *
之外,最好也不要濫用 .
來匹配任意字元,因為絕大部分情況你想要的都不是任意字元,而是數字(\d)、英數字(\w)、或者範圍更大的非空白字元(\S)
如果像上面舉的例子直接用 .+@.+
來驗證信箱的話,可能就會誤把 hello world@gmail.com
存進資料庫,進而導致寄信時奇怪的 bug
很多人在寫 regex 驗證字串時會忘記把頭尾的 ^
跟 $
寫進去,但因為 regex 在匹配時只要有子字串符合就可以了,因此判斷出來的結果會跟原本想的不太一樣
譬如說我想用 \d{5}
來檢查輸入是不是五位數字,是的話就有可能是台灣的郵遞區號,但如果我直接寫 \d{5}
的話可能會不小心接受了更長的字串,必須要寫成 ^\d{5}$
才行
const badRegex = /\d{5}/
const goodRegex = /^\d{5}$/
badRegex.test("12345") // true
badRegex.test("1234567") // true(明明有七個數字卻被判定為 true)
goodRegex.test("12345") // true
goodRegex.test("1234567") // false(只允許五個數字所以判定為 false)
因為各個語言都有 validator 這種 library,所以一般來說在做 input validation 不太會需要手刻 regular expression,只需要呼叫別人寫好的 isEmail()
、isDate()
就好了
但因為還是有少數情況需要自己寫 regex,所以這時候就得小心謹慎一點,才不會一個不小心就讓怪怪的資料進到資料庫(我就曾經這樣過XD),到時候再要清理就真的很麻煩~